/*
 * 文件名:CAudio.cpp
 *
 * 	 功能描述:
 *  创建日期: 2010-11-28
 *  版本Ver0.01
 *  作者: huanying2000
 *  版权:copyright 2010 by the Camellia project 
 *  free software under the terms of the LGPL 2.1
 */

#include "CAudio.hpp"

CAudio::CAudio(CPlayList *playList, string cpuAttr)
{
	int result;
	long parr;
	int libpar = 0;

	if(mpg123_init() != MPG123_OK)
	{
		cerr << "Cannot initialize mpg123 library:" << mpg123_plain_strerror(result) << "!\n";
		throw string("mpg123_init() failure!");
	}

	strcpy(param.cpu, cpuAttr.c_str());

	/* This may get leaked on premature exit(), which is mainly a cosmetic issue... */
	mp = mpg123_new_pars(&result);
	if(mp == NULL)
	{
		cerr << "Crap! Cannot get mpg123 parameters:" << mpg123_plain_strerror(result) << "!\n";
		throw string("mpg123_new_pars() failure!");
	}

	/* get default values */
	mpg123_getpar(mp, MPG123_DOWN_SAMPLE, &parr, NULL);
	param.down_sample = static_cast<int>(parr);

	mpg123_getpar(mp, MPG123_RVA, &param.rva, NULL);
	mpg123_getpar(mp, MPG123_DOWNSPEED, &param.halfspeed, NULL);
	mpg123_getpar(mp, MPG123_UPSPEED, &param.doublespeed, NULL);
	mpg123_getpar(mp, MPG123_OUTSCALE, &param.outscale, NULL);
	mpg123_getpar(mp, MPG123_FLAGS, &parr, NULL);
	mpg123_getpar(mp, MPG123_INDEX_SIZE, &param.index_size, NULL);
	param.flags = static_cast<int>(parr);
	param.flags |= MPG123_SEEKBUFFER; /* Default on, for HTTP streams. */
	mpg123_getpar(mp, MPG123_RESYNC_LIMIT, &param.resync_limit, NULL);
	mpg123_getpar(mp, MPG123_PREFRAMES, &param.preframes, NULL);

	if( !(MPG123_OK == (result = mpg123_par(mp, MPG123_VERBOSE, param.verbose, 0))
	&& ++libpar
	&& MPG123_OK == (result = mpg123_par(mp, MPG123_FLAGS, param.flags, 0))
	&& ++libpar
	&& MPG123_OK == (result = mpg123_par(mp, MPG123_DOWN_SAMPLE, param.down_sample, 0))
	&& ++libpar
	&& MPG123_OK == (result = mpg123_par(mp, MPG123_RVA, param.rva, 0))
	&& ++libpar
	&& MPG123_OK == (result = mpg123_par(mp, MPG123_FORCE_RATE, param.force_rate, 0))
	&& ++libpar
	&& MPG123_OK == (result = mpg123_par(mp, MPG123_DOWNSPEED, param.halfspeed, 0))
	&& ++libpar
	&& MPG123_OK == (result = mpg123_par(mp, MPG123_UPSPEED, param.doublespeed, 0))
	&& ++libpar
	&& MPG123_OK == (result = mpg123_par(mp, MPG123_ICY_INTERVAL, 0, 0))
	&& ++libpar
	&& MPG123_OK == (result = mpg123_par(mp, MPG123_RESYNC_LIMIT, param.resync_limit, 0))
	&& ++libpar
	&& MPG123_OK == (result = mpg123_par(mp, MPG123_TIMEOUT, param.timeout, 0))
	&& ++libpar
	&& MPG123_OK == (result = mpg123_par(mp, MPG123_OUTSCALE, param.outscale, 0))
	&& ++libpar
	&& MPG123_OK == (result = mpg123_par(mp, MPG123_PREFRAMES, param.preframes, 0))
	) )
	{
		cerr << "Cannot set library parameter " << libpar << " : " << mpg123_plain_strerror(result) << "!\n";
		throw string("mpg123_par() failure!");
	}

	//获得mpg123_handle
	if ( (mh = mpg123_parnew(mp, param.cpu, &result))== NULL)
	//if ( (mh = mpg123_new(NULL, NULL)) == NULL)
	{
		cerr << "Crap! Cannot get a mpg123 handle: " << mpg123_plain_strerror(result) << "!\n";
		mpg123_delete_pars(mp);
		throw string("mpg123_parnew() failure!");
	}

	sem.initSemaphore();			//初始化信号量
	pPlayList = playList;
	status = STATUS_PLAY;
}

CAudio::~CAudio()
{
	sem.rmSemaphore();

	mpg123_delete_pars(mp);
	mpg123_close(mh);
	mpg123_exit();
}

bool CAudio::open()
{
	traceName = pPlayList->getFileName(CPlayList::CURR);

	if(mpg123_open(mh, traceName.c_str()) != MPG123_OK)
	{
		 cerr << "Cannot open " << traceName.c_str() << " : " << mpg123_strerror(mh) << "!\n";
		 return false;
	}

	return true;
}

int CAudio::play()
{
	/*childPid = fork();

	if (0 == childPid)
	{
		signal(SIGTERM, catchSignal);

		char *progName = "subPlayPro";
		//Linux-specific since Linux 2.6.9
		if (-1 ==prctl(PR_SET_NAME, progName, 0, 0, 0, 0))
		{
			cerr << "prctl error[" << errno << "]:" << strerror(errno) << '\n';
		}
		subPlay();
	}
	else if (childPid > 0)
	{
		signal(SIGCHLD, catchSignal);
		signal(SIGUSR1, catchSignal);
		//close(pComm->fdArray[READ_FD]);
		//sem.setSemValue(1);
		sem.setSemValue(0);
		//control();
		//waitpid(childPid, 0, 0);
		//sem.rmSemaphore();
		//shm.rmShm();
	}
	else
	{
		cerr << "fork error!\n";
		return -1;
	}*/

	pthread_t playThreadId;
	//pthread_t controlThreadId;

	pthread_create(&playThreadId, NULL, playThread, static_cast<void *>(this));
	//pthread_create(&controlThreadId, NULL, controlThread, static_cast<void *>(this));

	//pthread_join(playThreadId, NULL);
	//pthread_join(controlThreadId, NULL);
	pthread_detach(playThreadId);
	//pthread_detach(controlThreadId);

	return 0;
}

void CAudio::pause()
{
	status = STATUS_PAUSE;
	//sem.semaphoreP();
}

void CAudio::unpause()
{
	status = STATUS_UNPAUSE;
	sem.semaphoreV();
	//play();
}

void CAudio::stop()
{
	status = STATUS_STOP;
	//sem.semaphoreP();
}

void CAudio::prev()
{
	traceName = pPlayList->getFileName(CPlayList::PREV);
	status = STATUS_NEXT;
}

void CAudio::next()
{
	traceName = pPlayList->getFileName(CPlayList::NEXT);
	status = STATUS_NEXT;
}

void CAudio::seek(off_t offset)
{
	seekFrames = offset;
	status = STATUS_SEEK;
}

void CAudio::changeVolumn(const double change)
{
	double base;
	double really;
	double rvadb;
	//mpg123_volume(mh, change);
	//mpg123_volume_change(mh, change);

	mpg123_getvolume(mh, &base, &really, &rvadb);

	cout << "base:" << base << ", really:" << really << ", rvadb:" << rvadb << '\n';
}

inline long CAudio::pitchRate(long rate)
{
	//return param.pitch == 0 ? (rate) : (long) ((param.pitch+1.0)*(rate));
	return rate;
}

inline CAudio::ENStatus CAudio::getPlayStatus(void) const
{
	return status;
}

bool CAudio::getAudioInfo(KAudioInfo *pAudioInfo)
{
	off_t simpleNumbers;			//整个音频采样个数
	int duration;							//整个音频持续时间，单位秒
	double timePerFrame;			//播放每帧所需要的时间，单位秒
	mpg123_frameinfo frameInfo;

	if (MPG123_ERR == (simpleNumbers = mpg123_length(mh)) )
	{
		cerr << "Get sample numbers error!\n";
		return false;
	}

	if (MPG123_ERR == mpg123_info(mh, &frameInfo))
	{
		cerr << "Get audio info error!\n";
		return false;
	}

	//必须放在mpg123_length函数之后
	if ((pAudioInfo->timePerFrame = mpg123_tpf(mh)) < 0)
	{
		cerr << "Get tpf error!\n";
		return false;
	}

	switch (frameInfo.version)
	{
		case 0:
			strcpy(pAudioInfo->mpegVersion, "1.0");
			break;

		case 1:
			strcpy(pAudioInfo->mpegVersion, "2.0");
			break;

		case 2:
			strcpy(pAudioInfo->mpegVersion, "2.5");
			break;

		default:
			strcpy(pAudioInfo->mpegVersion, "0.0");
			break;
	}

	switch (frameInfo.layer)
	{
		case 1:
			strcpy(pAudioInfo->mpegLayer, "MP1");
			break;

		case 2:
			strcpy(pAudioInfo->mpegLayer, "MP2");
			break;

		case 3:
			strcpy(pAudioInfo->mpegLayer, "MP3");
			break;

		default:
			strcpy(pAudioInfo->mpegLayer, "MP0");
			break;
	}

	switch (frameInfo.mode)
	{
		case 0:
			strcpy(pAudioInfo->mode, "STEREO");
			break;

		case 1:
			strcpy(pAudioInfo->mode, "JOINT");
			break;

		case 2:
			strcpy(pAudioInfo->mode, "DUAL");
			break;

		case 3:
			strcpy(pAudioInfo->mode, "MONO");
			break;
	}

	pAudioInfo->sampleRate = frameInfo.rate;
	pAudioInfo->bitrRate = frameInfo.bitrate;
	pAudioInfo->duration = simpleNumbers / frameInfo.rate;

	return true;
}

bool CAudio::getTagInfo(KAudioTag *pAudioTag)
{
	ifstream input(traceName.c_str());
	CAudioTag audioTag(&input);
	audioTag.getTags(pAudioTag);
}

off_t CAudio::getPlayedFrames()
{
	return mpg123_tellframe(mh);
}

int CAudio::subPlay()
{
	int result;
	size_t donePosition;
	const size_t OUTPUTMEMSIZE = 8192;
	unsigned char outputMem[OUTPUTMEMSIZE];
	long rate;
	int channels;
	int format;
	unsigned char *audio;
	size_t bytes;
	off_t framenum;

	KAudioOutput kAudioOutput;
	kAudioOutput.fn = -1;
	kAudioOutput.rate = -1;
	kAudioOutput.userptr = NULL;
	kAudioOutput.device = NULL;
	kAudioOutput.channels = -1;
	kAudioOutput.format = -1;
	kAudioOutput.flags = 0;

	COutput *pOutput = new CAlsa;
	pOutput->openOutput(&kAudioOutput);

	while (1)
	{
		switch(status)
		{
			case STATUS_UNPAUSE:
				 if (mpg123_seek_frame(mh, frameOff, SEEK_SET) < 0)
				 {
					 fprintf(stderr, "mpg123_seek_frame error:%s\n", mpg123_strerror(mh));
					 return -1;
				 }
				 status = STATUS_PLAY;
				 break;

			case STATUS_SEEK:
				if (mpg123_seek_frame(mh, seekFrames, SEEK_SET) < 0)
				 {
					 fprintf(stderr, "mpg123_seek_frame error:%s\n", mpg123_strerror(mh));
					 return -1;
				 }
				 status = STATUS_PLAY;
				 break;

			case STATUS_PAUSE:
				frameOff = mpg123_tellframe(mh);
				//cout << "frameOff is " << frameOff << '\n';
				sem.semaphoreP();
				//return 0;
				continue;

			case STATUS_STOP:
				frameOff = 0;
				sem.semaphoreP();
				//return 0;
				continue;

			case STATUS_QUIT:
				return 1;

			case VOL_INC:
				//setVolumn(0.75);
				//alsa.setVolume(pComm->seekFrames);
				status = STATUS_PLAY;
				break;

			case VOL_DEC:
				//setVolumn(-0.75);
				//alsa.setVolume(pComm->seekFrames);
				status = STATUS_PLAY;
				break;

			case STATUS_NEXT:
				mpg123_delete(mh);

				if ( (mh = mpg123_parnew(mp, param.cpu, &result))== NULL)
				{
					cerr << "Crap! Cannot get a mpg123 handle: " << mpg123_plain_strerror(result) << "!\n";
					mpg123_delete_pars(mp);
					return 77;
				}


				if(mpg123_open(mh, traceName.c_str()) != MPG123_OK)
				{
					 cerr << "Cannot open " << traceName.c_str() << " : " << mpg123_strerror(mh) << "!\n";
					 return 79;
				}
				if (MPG123_OK != mpg123_param(mh, MPG123_VERBOSE, 1, 0))
				{
					cerr << "mpg123_param error!\n";
				}

				pOutput->flushOutput(&kAudioOutput);
				mpg123_getformat(mh, &rate, &channels, &format);
				kAudioOutput.rate = pitchRate(rate);
				kAudioOutput.channels = channels;
				kAudioOutput.format   = format;
				pOutput->closeOutput(&kAudioOutput);
				pOutput->openOutput(&kAudioOutput);

				sleep(1);				//等待1秒钟后开始播放下一首

				status = STATUS_PLAY;

				break;

		}

		result =  mpg123_read(mh, outputMem, OUTPUTMEMSIZE,  &donePosition);

		if (pOutput->writeOutput(&kAudioOutput, outputMem, OUTPUTMEMSIZE) < 0)
		{
			fprintf(stderr, "audio output error:%s\n", strerror(errno));
			return -1;
		}

		switch(result)
		{
			case MPG123_ERR :
				fprintf(stderr, "mpg123_read error:%s\n", mpg123_strerror(mh));
				return -1;

			case MPG123_DONE :
				//pthread_t threadId;
				printf("mpg123_read : MPG123_DONE!\n");

				//pthread_create(&threadId, NULL, &sendDoneSignal, NULL);
				//pthread_detach(threadId);
				//sem.getSemStat();
				//cout << "GETNCNT:" << sem.getSemNCnt() << "!\n";
				//sem.semaphoreP();
				sleep(1);				//等待1秒钟后开始播放下一首
				next();
				break;

			case MPG123_NO_SPACE:
				fprintf(stderr, "Do not have enough space!\n");
				return -1;

			case MPG123_NEW_FORMAT :
				printf("mpg123_read:MPG123_NEW_FORMAT\n");
				sleep(1);
				mpg123_getformat(mh, &rate, &channels, &format);
				kAudioOutput.rate = pitchRate(rate);
				kAudioOutput.channels = channels;
				kAudioOutput.format   = format;
				pOutput->closeOutput(&kAudioOutput);
				pOutput->openOutput(&kAudioOutput);
				break;
		}
	}//end of while

	if (pOutput)
		delete pOutput;

	//mpg123_delete_pars(mp);
	//mpg123_close(mh);
	return 0;
}

CAudio *pAudio = 0;

void catchSignal(int signo)
{
    switch (signo)
    {
    	case SIGUSR1:
#ifdef DEBUG
    		cout << "pAudio address:" << pAudio << '\n';
#endif
    		assert(pAudio);
    		cout << "catch SIGUSER1 signal!\n";
    		pAudio->next();
    		break;

    	case SIGCHLD:
    		cout << "catch child terminal signal!\n";
			while (waitpid(-1, NULL, WNOHANG) > 0)
						;
			break;


    		break;
    }
}

void *playThread(void *args)
{
	CAudio *audio = static_cast<CAudio *>(args);
	audio->subPlay();
}

void *controlThread(void *args)
{
	CAudio *audio = static_cast<CAudio *>(args);
	audio->sem.setSemValue(0);
	//audio->control();
}

void *sendDoneSignal(void *args)
{
	cout << "in sendDoneSignal function parent pid:" << getppid() << '\n';
	kill(getpid(), SIGUSR1);
}
